## install finrl library
#%pip install git+https://github.com/AI4Finance-LLC/FinRL-Library.git

import sys
import warnings
warnings.filterwarnings('ignore')
import talib as ta
from utils import process_future_data, dates_intersection, add_covariance, StockPortfolioEnv, create_features
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('Agg')
# %matplotlib inline
import datetime

from finrl import config
from finrl import config_tickers
from finrl.meta.preprocessor.yahoodownloader import YahooDownloader
from finrl.meta.preprocessor.preprocessors import FeatureEngineer, data_split
from finrl.meta.env_portfolio_allocation.env_portfolio import StockPortfolioEnv
from finrl.agents.stablebaselines3.models import DRLAgent
from finrl.plot import backtest_stats, backtest_plot, get_daily_return, get_baseline,convert_daily_return_to_pyfolio_ts
from finrl.meta.data_processor import DataProcessor
from finrl.meta.data_processors.processor_yahoofinance import YahooFinanceProcessor

sys.path.append("../FinRL-Library")

import os
if not os.path.exists("./" + config.DATA_SAVE_DIR):
    os.makedirs("./" + config.DATA_SAVE_DIR)
if not os.path.exists("./" + config.TRAINED_MODEL_DIR):
    os.makedirs("./" + config.TRAINED_MODEL_DIR)
if not os.path.exists("./" + config.TENSORBOARD_LOG_DIR):
    os.makedirs("./" + config.TENSORBOARD_LOG_DIR)
if not os.path.exists("./" + config.RESULTS_DIR):
    os.makedirs("./" + config.RESULTS_DIR)

rice = process_future_data('./rice.csv')
lc   = process_future_data('./live_cattle.csv')
cf   = process_future_data('./coffee.csv')
eth  = process_future_data('./ethanol.csv')
corn = process_future_data('./corn.csv')
iron   = process_future_data('./iron.csv')
gold = process_future_data('./gold.csv')
oil = process_future_data('./oil.csv')
soy  = process_future_data('./soybean.csv')
wht  = process_future_data('./wheat.csv')

cmds_f =pd.concat([rice,lc,cf,eth,corn,iron,gold,oil,soy,wht],axis=0)

cmds_f['tic'].unique().tolist()

cmds_f_l = cmds_f["tic"].unique().tolist()
stocks_br  = ["PETR4.SA", "VALE3.SA", "ITUB4.SA", "MGLU3.SA", "BBAS3.SA", "BBDC4.SA","B3SA3.SA", "PETR3.SA", "RENT3.SA", "ELET3.SA" ]
stocks_usa = ["META", "AAPL", "AMZN", "F", "T", "BAC", "GOOGL", "MSFT", "INTC", "CMCSA"]
stocks_eur = ["iSP.MI", "ENEL.MI", "SAN.MC", "INGA.AS", "ENI.MI", "BBVA.MC", "IBE.MC", "CS.PA", "STLA.MI","DTE.DE"]
stocks_chn = ["601899.SS","600010.SS","600795.SS", "603993.SS", "600157.SS", "601288.SS", "600050.SS", "601398.SS", "600537.SS","600777.SS"]

ativos = list(set().union(stocks_br,stocks_usa,stocks_eur,stocks_chn))

len(ativos)

dp = YahooFinanceProcessor()
df = dp.download_data(start_date = '2004-01-01',
                     end_date = '2022-11-07',
                     ticker_list = ativos, time_interval='1D')
ativos = list(set().union(stocks_br,stocks_usa,stocks_eur,stocks_chn,cmds_f_l))

df = pd.concat([df,cmds_f],axis=0)
df['date']= pd.to_datetime(df['date'])
df = df.sort_values(by='date')
df = add_covariance(df)
df = create_features(df)

dates_f3 = dates_intersection(df)
df=df[df['date'].isin(dates_f3)]

train = data_split(df, '2013-01-04','2018-01-01')
trade = data_split(df,'2018-01-02', '2022-10-27')
stock_dimension = len(train.tic.unique())
state_space = stock_dimension

features =['RSI', 'slowk', 'slowd', 'WILLR', 'MACD','ROC', 'OBV', 'lag_20', 'lag_40', 'lag_60']

env_kwargs = {
    "hmax": 100,
    "initial_amount": 1000000,
    "transaction_cost_pct": 0,
    "state_space": state_space,
    "stock_dim": stock_dimension,
    "tech_indicator_list": features,
    "action_space": stock_dimension,
    "reward_scaling": 1e-4
}

e_train_gym = StockPortfolioEnv(df = train, **env_kwargs)
env_train, _ = e_train_gym.get_sb_env()
agent = DRLAgent(env = env_train)

#Model 1: **A2C**
agent = DRLAgent(env = env_train)

A2C_PARAMS = {"n_steps": 5, "ent_coef": 0.005, "learning_rate": 0.0002}
model_a2c = agent.get_model(model_name="a2c",model_kwargs = A2C_PARAMS)
trained_a2c = model_a2c.load('trained_models/trained_a2c.zip')

# Model 2: **PPO**
agent = DRLAgent(env = env_train)
PPO_PARAMS = {
    "n_steps": 2048,
    "ent_coef": 0.005,
    "learning_rate": 0.0001,
    "batch_size": 128,
}
model_ppo = agent.get_model("ppo",model_kwargs = PPO_PARAMS)
trained_ppo = model_ppo.load('trained_models/trained_ppo.zip')

# Model 3: **DDPG**
agent = DRLAgent(env = env_train)
DDPG_PARAMS = {"batch_size": 128, "buffer_size": 50000, "learning_rate": 0.001}
model_ddpg = agent.get_model("ddpg",model_kwargs = DDPG_PARAMS)
trained_ddpg = model_ddpg.load('trained_models/trained_ddpg.zip')

# Model 4: **SAC**
agent = DRLAgent(env = env_train)
SAC_PARAMS = {
    "batch_size": 128,
    "buffer_size": 100000,
    "learning_rate": 0.0003,
    "learning_starts": 100,
    "ent_coef": "auto_0.1",
}
model_sac = agent.get_model("sac",model_kwargs = SAC_PARAMS)
trained_sac = model_sac.load('trained_models/trained_sac.zip')

# Model 5: **TD3*
agent = DRLAgent(env = env_train)
TD3_PARAMS = {"batch_size": 100,
              "buffer_size": 1000000,
              "learning_rate": 0.001
}
model_td3 = agent.get_model("td3",model_kwargs = TD3_PARAMS)
trained_td3 = model_td3.load('trained_models/trained_td3.zip')

# Trading
e_trade_gym = StockPortfolioEnv(df = trade, **env_kwargs)
df_daily_return, df_actions = DRLAgent.DRL_prediction(model=trained_sac,
                        environment = e_trade_gym)

df_daily_return2, df_actions2 = DRLAgent.DRL_prediction(model=trained_ddpg,
                        environment = e_trade_gym)

df_daily_return3, df_actions3 = DRLAgent.DRL_prediction(model=trained_ppo,
                        environment = e_trade_gym)

df_daily_return4, df_actions4 = DRLAgent.DRL_prediction(model=trained_a2c,
                        environment = e_trade_gym)

df_daily_return5, df_actions5 = DRLAgent.DRL_prediction(model=trained_td3,
                        environment = e_trade_gym)

df_daily_return.to_csv('results/df_daily_return_sac.csv')
df_daily_return2.to_csv('results/df_daily_return_ddpg.csv')
df_daily_return3.to_csv('results/df_daily_return_ppo.csv')
df_daily_return4.to_csv('results/df_daily_return_a2c.csv')
df_daily_return5.to_csv('results/df_daily_return_td3.csv')

df_actions.to_csv( 'results/df_actions_sac.csv')
df_actions2.to_csv('results/df_actions_ddpg.csv')
df_actions3.to_csv('results/df_actions_ppo.csv')
df_actions4.to_csv('results/df_actions_a2c.csv')
df_actions5.to_csv('results/df_actions_td3.csv')

# BackTestStats
pass in df_account_value, this information is stored in env class

from pyfolio import timeseries
DRL_strat = convert_daily_return_to_pyfolio_ts(df_daily_return)
perf_func = timeseries.perf_stats
perf_stats_all = perf_func( returns=DRL_strat,
                              factor_returns=DRL_strat,
                                positions=None, transactions=None, turnover_denom="AGB")

import pyfolio as pf

pf.create_full_tear_sheet(DRL_strat)
df_comp = trade[['date','close','tic']]
df_comp.set_index('date',inplace=True)
res = df_comp.pivot(columns='tic', values='close')

# Asset weights
weight = res.shape[1]
wts = np.full(shape=weight, fill_value=1/weight).tolist()
ret_data = res.pct_change()

weighted_returns = (1/weight * ret_data)
weighted_returns.index= pd.to_datetime(weighted_returns.index)
weighted_returns

ptf_return= (weighted_returns.sum(axis=1,skipna=False))
ptf_return.name= 'Portfolio Returns'
ptf_return.index=DRL_strat.index

baseline_df = get_baseline(
        ticker='^DJI', start=df_daily_return5.loc[0,'date'], end='2022-10-27'
    )

baseline_returns = get_daily_return(baseline_df, value_col_name="close")

with pf.plotting.plotting_context(font_scale=1.1):
        pf.create_full_tear_sheet(returns = DRL_strat,
                                       benchmark_rets=baseline_returns, set_context=False)

with pf.plotting.plotting_context(font_scale=1.1):
        pf.create_full_tear_sheet(returns = DRL_strat,
                                       benchmark_rets=ptf_return, set_context=False)

"""## Min-Variance Portfolio Allocation"""

from pypfopt.efficient_frontier import EfficientFrontier
from pypfopt import risk_models

#calculate_portfolio_minimum_variance
unique_tic = trade.tic.unique()
unique_trade_date = trade.date.unique()

portfolio = pd.DataFrame(index = range(1), columns = unique_trade_date)
initial_capital = 1000000
portfolio.loc[0,unique_trade_date[0]] = initial_capital

for i in range(len( unique_trade_date)-1):
    df_temp = trade[trade.date==unique_trade_date[i]].reset_index(drop=True)
    df_temp_next = trade[trade.date==unique_trade_date[i+1]].reset_index(drop=True)
    Sigma = df_temp.return_list[0].cov()
    #portfolio allocation
    ef_min_var = EfficientFrontier(None, Sigma,weight_bounds=(0, 0.1))
    #minimum variance
    raw_weights_min_var = ef_min_var.min_volatility()
    #get weights
    cleaned_weights_min_var = ef_min_var.clean_weights()

    #current capital
    cap = portfolio.iloc[0, i]
    #current cash invested for each stock
    current_cash = [element * cap for element in list(cleaned_weights_min_var.values())]
    # current held shares
    current_shares = list(np.array(current_cash)
                                      / np.array(df_temp.close))
    # next time period price
    next_price = np.array(df_temp_next.close)
    ##next_price * current share to calculate next total account value
    portfolio.iloc[0, i+1] = np.dot(current_shares, next_price)

portfolio=portfolio.T
portfolio.columns = ['account_value']
min_var_cumpod =(portfolio.account_value.pct_change()+1).cumprod()-1
ptf_return.index=df_daily_return.date
ptf_return.to_csv('results/df_daily_return_ptf.csv')
portfolio.account_value.pct_change().to_csv('results/df_daily_return_minvar.csv')
sac_cumpod = (df_daily_return.daily_return+1).cumprod()-1
ddpg_cumpod  = (df_daily_return2.daily_return+1).cumprod()-1
ppo_cumpod  = (df_daily_return3.daily_return+1).cumprod()-1
a2c_cumpod   = (df_daily_return4.daily_return+1).cumprod()-1
td3_cumpod = (df_daily_return5.daily_return+1).cumprod()-1
dji_cumpod =(baseline_returns.fillna(0)+1).cumprod()-1

ptf_cumpod =(ptf_return.fillna(0)+1).cumprod()-1


#Plots
from datetime import datetime as dt

import matplotlib.pyplot as plt
import plotly
import plotly.graph_objs as go

time_ind = pd.Series(df_daily_return.date)
trace0_portfolio = go.Scatter(x = time_ind, y = a2c_cumpod, mode = 'lines', name = 'A2C')
trace1_portfolio = go.Scatter(x = time_ind, y = dji_cumpod, mode = 'lines', name = 'DJIA')
trace2_portfolio = go.Scatter(x = time_ind, y = min_var_cumpod, mode = 'lines', name = 'Min-Variance')
trace3_portfolio = go.Scatter(x = time_ind, y = ptf_cumpod, mode = 'lines', name = 'Portfolio Buy & Hold')
trace4_portfolio = go.Scatter(x = time_ind, y = ddpg_cumpod, mode = 'lines', name = 'DDPG')
trace5_portfolio = go.Scatter(x = time_ind, y = sac_cumpod, mode = 'lines', name = 'SAC')
trace6_portfolio = go.Scatter(x = time_ind, y = ppo_cumpod, mode = 'lines', name = 'PPO')
trace7_portfolio = go.Scatter(x = time_ind, y = td3_cumpod, mode = 'lines', name = 'TD3')

fig = go.Figure()
fig.add_trace(trace0_portfolio)
fig.add_trace(trace1_portfolio)
fig.add_trace(trace2_portfolio)
fig.add_trace(trace3_portfolio)
fig.add_trace(trace4_portfolio)
fig.add_trace(trace5_portfolio)
fig.add_trace(trace6_portfolio)
fig.add_trace(trace7_portfolio)

fig.update_layout(
    legend=dict(
        x=0,
        y=1,
        traceorder="normal",
        font=dict(
            family="arial",
            size=15,
            color="black"
        ),
        bgcolor="White",
        bordercolor="white",
        borderwidth=2

    ),
)

fig.update_layout(title={
        'y':0.85,
        'x':0.85,
        'xanchor': 'center',
        'yanchor': 'top'})
fig.update_layout(
    xaxis_title="Data",
    yaxis_title="Retorno Acumulado",

    xaxis={'type': 'date',
       'tick0': time_ind[0],
        'tickmode': 'linear',
       'dtick': 86400000.0 *80}

)
fig.update_xaxes(showline=True,linecolor='black',showgrid=True, gridwidth=1, gridcolor='LightSteelBlue',mirror=True)
fig.update_yaxes(showline=True,linecolor='black',showgrid=True, gridwidth=1, gridcolor='LightSteelBlue',mirror=True)
fig.update_yaxes(zeroline=True, zerolinewidth=1, zerolinecolor='LightSteelBlue')
fig.show()